#include "logger.h"
#include <QDir>
#include <QDebug>
#include <iostream>

Logger *Logger::m_instance = nullptr;
QMutex Logger::m_mutex;

Logger::Logger(QObject *parent)
    : QObject(parent), m_logLevel(LogLevel::Info), m_enableConsole(true), m_maxFileSize(10 * 1024 * 1024) // 10MB
{
}

Logger::~Logger()
{
    close();
}

// 获取日志单例
Logger *Logger::instance()
{
    if (m_instance == nullptr)
    {
        QMutexLocker locker(&m_mutex);
        if (m_instance == nullptr)
        {
            m_instance = new Logger();
        }
    }
    return m_instance;
}

// 初始化日志系统
void Logger::initialize(const QString &logFilePath, LogLevel level, bool enableConsole)
{
    QMutexLocker locker(&m_mutex);

    m_logFilePath = logFilePath;
    m_logLevel = level;
    m_enableConsole = enableConsole;

    // 确保日志目录存在
    QFileInfo fileInfo(logFilePath);
    QDir dir = fileInfo.absoluteDir();
    if (!dir.exists())
    {
        dir.mkpath("."); // 创建日志目录
        qDebug() << "创建日志目录:" << dir.absolutePath();
    }

    // 打开日志文件
    if (m_logFile.isOpen())
    {
        m_logFile.close();
    }

    m_logFile.setFileName(logFilePath);
    if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
    {
        m_stream.setDevice(&m_logFile);

        // 写入启动标记
        QString startMsg = QString("\n========== start time: %1 ==========\n")
                               .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
        m_stream << startMsg;
        m_stream.flush();

        if (m_enableConsole)
        {
            std::cout << startMsg.toStdString() << std::endl;
        }
    }
    else
    {
        qWarning() << "无法打开日志文件:" << logFilePath;
    }
}

// 写入日志
void Logger::log(LogLevel level, const QString &message, const QString &file, int line)
{
    // 检查日志级别
    if (level < m_logLevel)
    {
        return;
    }

    QMutexLocker locker(&m_mutex);

    // 检查日志轮转
    checkLogRotation();

    // 构建日志消息
    QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
    QString levelStr = levelToString(level);
    QString logMsg;

    if (!file.isEmpty() && line > 0)
    {
        // 提取文件名（不含路径）
        QString fileName = QFileInfo(file).fileName();
        logMsg = QString("[%1] [%2] [%3:%4] %5")
                     .arg(timestamp)
                     .arg(levelStr)
                     .arg(fileName)
                     .arg(line)
                     .arg(message);
    }
    else
    {
        logMsg = QString("[%1] [%2] %3")
                     .arg(timestamp)
                     .arg(levelStr)
                     .arg(message);
    }

    // 写入文件
    if (m_logFile.isOpen())
    {
        m_stream << logMsg << "\n";
        m_stream.flush();
    }

    // 输出到控制台
    if (m_enableConsole)
    {
        // 根据级别使用不同的输出方式
        switch (level)
        {
        case LogLevel::Debug:
            qDebug().noquote() << logMsg;
            break;
        case LogLevel::Info:
            qInfo().noquote() << logMsg;
            break;
        case LogLevel::Warning:
            qWarning().noquote() << logMsg;
            break;
        case LogLevel::Error:
        case LogLevel::Critical:
            qCritical().noquote() << logMsg;
            break;
        }
    }

    // 发送信号
    emit logMessage(level, logMsg);
}

// 写入调试日志
void Logger::debug(const QString &message)
{
    log(LogLevel::Debug, message);
}

void Logger::debug(const QString &module, const QString &message)
{
    log(LogLevel::Debug, QString("[%1] %2").arg(module).arg(message));
}

// 写入信息日志
void Logger::info(const QString &message)
{
    log(LogLevel::Info, message);
}

void Logger::info(const QString &module, const QString &message)
{
    log(LogLevel::Info, QString("[%1] %2").arg(module).arg(message));
}

// 写入警告日志
void Logger::warning(const QString &message)
{
    log(LogLevel::Warning, message);
}

void Logger::warning(const QString &module, const QString &message)
{
    log(LogLevel::Warning, QString("[%1] %2").arg(module).arg(message));
}

// 写入错误日志
void Logger::error(const QString &message)
{
    log(LogLevel::Error, message);
}

void Logger::error(const QString &module, const QString &message)
{
    log(LogLevel::Error, QString("[%1] %2").arg(module).arg(message));
}

// 写入严重错误日志
void Logger::critical(const QString &message)
{
    log(LogLevel::Critical, message);
}

void Logger::critical(const QString &module, const QString &message)
{
    log(LogLevel::Critical, QString("[%1] %2").arg(module).arg(message));
}

// 关闭日志
void Logger::close()
{
    QMutexLocker locker(&m_mutex);

    if (m_logFile.isOpen())
    {
        QString endMsg = QString("========== 程序退出 %1 ==========\n")
                             .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
        m_stream << endMsg;
        m_stream.flush();
        m_logFile.close();
    }
}

// 将日志级别转换为字符串
QString Logger::levelToString(LogLevel level) const
{
    switch (level)
    {
    case LogLevel::Debug:
        return "DEBUG   ";
    case LogLevel::Info:
        return "INFO    ";
    case LogLevel::Warning:
        return "WARNING ";
    case LogLevel::Error:
        return "ERROR   ";
    case LogLevel::Critical:
        return "CRITICAL";
    default:
        return "UNKNOWN ";
    }
}

// 检查日志轮转
void Logger::checkLogRotation()
{
    if (!m_logFile.isOpen())
    {
        return;
    }

    // 检查文件大小
    if (m_logFile.size() > m_maxFileSize)
    {
        // 关闭当前文件
        m_logFile.close();

        // 重命名旧文件
        QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
        QString backupPath = m_logFilePath + "." + timestamp;
        QFile::rename(m_logFilePath, backupPath);

        // 重新打开新文件
        m_logFile.setFileName(m_logFilePath);
        if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
        {
            m_stream.setDevice(&m_logFile);
            QString rotateMsg = QString("========== 日志轮转 %1 ==========\n")
                                    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
            m_stream << rotateMsg;
            m_stream.flush();
        }
    }
}
